pacman::p_load(tidyverse, lubridate, here, skimr, yaml, ggplot2, ggalt, gplots,
               sf, rnaturalearth, rnaturalearthdata, maps, tools, RColorBrewer
               )

inputfile <- here::here("write/input/uw-chr-i213-public.csv.gz")

i213 <- read_delim(
    inputfile, delim = "|", 
    col_types = cols(
        # .default                     = col_character(),
        source                       = col_character(),
        sex                          = col_character(),
        cmplxn                       = col_character(),
        country_of_citizenship       = col_factor(),
        year                         = col_double(),
        month                        = col_double(),
        day                          = col_double(),
        hour                         = col_double(),
        minute                       = col_double(),
        fy                           = col_double(),
        age                          = col_double(),
        accompanied_juvenile_flag    = col_double(),
        unaccompanied_juvenile_flag  = col_double(),
        custody_redetermination_flag = col_double()
))

Descriptive analysis of UWCHR I-213 collection

As part of its “Human Rights At Home” and “Immigrant Rights Observatory” research initiatives, the University of Washington Center for Human Rights (UWCHR) has obtained a collection of I-213 “Record of Deportable/Inadmissible Alien” forms via Freedom of Information Act (FOIA) requests to the US Department of Homeland Security (DHS). These internal forms documenting apprehensions by US Immigration and Customs Enforcement (ICE) and Customs and Border Protection (CBP) are an important source of qualitative and quantitative information regarding immigration enforcement practices and patterns.

Under FOIA, UWCHR initially requested all I-213s produced by both CBP and ICE in the state of Washington from 2012-2017; when the agencies failed to adhere to the requirements of FOIA—first claiming privacy waivers were needed for every individual form and then claiming that as law enforcement agencies, they were exempt from providing such records—UWCHR sued in September 2018. UWCHR underwent separate settlement negotiations with each sub-agency. In these discussions, CBP agreed to provide a sample of all documents produced by staff in the agency’s Blaine and Spokane sectors from January 1, 2012 to September 30, 2017; and ICE agreed to provide copies of all documents produced from January 1, 2019 to March 31, 2020 by staff from the agency’s Seattle field office.

The resulting PDF forms were scraped and cleaned in a separate private repository developed by the Human Rights Data Analysis Group (HRDAG) and maintained by UWCHR. Cleaning and hand-coding of resulting data is ongoing; the following is a preliminary descriptive analysis of the collection. Maps of apprehension locations by I-213 at_near value and at_near county per capita were previously published for UWCHR’s August 2021 report “Protecting Immigrant Rights: Is Washington’s Law Working?”, see: https://uwchr.github.io/i-213-analysis/map-wa.html.

I-213 structure

Each record is made up of an I-213 “Record of Deportable/Inadmissible Alien” form collecting subject identification, biometrics, and apprehension characteristics data in a structured format (typographical variations may be introduced in the scraping process, we have attempted to standardize values in the cleaning process); followed by a semi-structured “Narrative” field containing additional description of the subject and apprehension, usually continued from the I-213 on one or more I-831 “Continuation Page” forms. Personally identifying information such as names and unique identifiers are redacted; narratives also include redactions under various FOIA exemptions. A small number of forms are fully redacted.

“Narrative” field contents are excluded from the public version of this dataset because they may contain sensitive information. Dataset fields following the naming convention mentions_* reflect simple str_detect() results for keywords in the “Narrative” text as described in the repository README.

While I-213 form contents are relatively structured, some fields may be left blank and the logic or meaning of codes used in some fields is obscure. For more details, see this ICE “I-213 Preparation” document released via FOIA; note that in some cases field contents differ from those described here.

Sample I-213 form

The following is a typical I-213 form representing an apprehension upon entry at Sea-Tac International Airport. Note redacted and blank I-213 fields, as well as fields filled with “See Narrative”. Most but not all text fields are scraped for the dataset analyzed here. Tick boxes are not scraped.

Sample I-831 form

The following is a typical I-831 “Continuation Page” associated with the above I-213 form, which continues the “Narrative” field. Other text fields here are not scraped, but we note that the “Title” field might provide details regarding the official responsible for the apprehension, though this information is also included in the narrative in this case.

I-213 narratives follow a semi-regular structure; we have scraped at_near values from the narrative where the “At Near” field reads “See Narrative”; and we have attempted to scrape “Current Criminal Charges” and “Current Administrative Charges” as charge_code_words, with lots of OCR inconsistencies. It appears that I-213 narratives are at least in part machine-generated rather than entered by hand: note the “null:” pointer preceding the description of the apprehension below.

Data overview

i213 %>% skimr::skim()
Data summary
Name Piped data
Number of rows 4054
Number of columns 64
_______________________
Column type frequency:
character 21
Date 1
factor 1
logical 27
numeric 14
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
file_hash 0 1.00 40 40 0 4054 0
source 2 1.00 3 3 0 2 0
installment 0 1.00 3 11 0 11 0
at_near 79 0.98 1 111 0 273 0
date_of_action 446 0.89 2 27 0 1163 0
location_code 604 0.85 3 15 0 66 0
sex 710 0.82 1 1 0 3 0
cmplxn 1118 0.72 1 12 0 16 0
last_entry 665 0.84 1 105 0 2050 0
method_location_apprehension 562 0.86 1 12 0 81 0
foreign_address 3921 0.03 17 1862 0 131 0
status_at_entry 3765 0.07 1 26 0 58 0
status_when_found 3765 0.07 1 22 0 56 0
mother_nationality 3363 0.17 4 30 0 62 0
father_nationality 3901 0.04 4 19 0 27 0
spouse_nationality 4016 0.01 4 13 0 8 0
number_nationality_minor_children 1579 0.61 1 57 0 305 0
disposition 836 0.79 1 35 0 76 0
charge_code_words 235 0.94 1 368 0 2480 0
state 238 0.94 2 2 0 10 0
county 238 0.94 6 21 0 70 0

Variable type: Date

skim_variable n_missing complete_rate min max median n_unique
date 39 0.99 2008-07-09 2020-04-30 2019-07-24 633

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
country_of_citizenship 0 1 FALSE 122 MEX: 2525, IND: 274, GUA: 267, HON: 140

Variable type: logical

skim_variable n_missing complete_rate mean count
cota_in_force 39 0.99 0.00 FAL: 4015
kww_in_force 39 0.99 0.64 TRU: 2581, FAL: 1434
mentions_airport 0 1.00 0.10 FAL: 3664, TRU: 390
mentions_anonymous_tip 0 1.00 0.01 FAL: 4023, TRU: 31
mentions_border_patrol 0 1.00 0.36 FAL: 2593, TRU: 1461
mentions_bus 0 1.00 0.03 FAL: 3924, TRU: 130
mentions_corrections 0 1.00 0.13 FAL: 3542, TRU: 512
mentions_courthouse 0 1.00 0.03 FAL: 3950, TRU: 104
mentions_database 0 1.00 0.30 FAL: 2854, TRU: 1200
mentions_detainer 0 1.00 0.32 FAL: 2759, TRU: 1295
mentions_family_unit 0 1.00 0.01 FAL: 4010, TRU: 44
mentions_greyhound 0 1.00 0.01 FAL: 4016, TRU: 38
mentions_hsi 0 1.00 0.04 FAL: 3882, TRU: 172
mentions_jail 0 1.00 0.47 FAL: 2158, TRU: 1896
mentions_juvenile 0 1.00 0.04 FAL: 3912, TRU: 142
mentions_license_plate 0 1.00 0.04 FAL: 3874, TRU: 180
mentions_no_criminal_history 0 1.00 0.05 FAL: 3853, TRU: 201
mentions_police 0 1.00 0.18 FAL: 3306, TRU: 748
mentions_previous_deport 0 1.00 0.23 FAL: 3137, TRU: 917
mentions_prison 0 1.00 0.19 FAL: 3270, TRU: 784
mentions_probation_office 0 1.00 0.00 FAL: 4035, TRU: 19
mentions_secure_comm 0 1.00 0.05 FAL: 3870, TRU: 184
mentions_sheriff 0 1.00 0.11 FAL: 3616, TRU: 438
mentions_state_patrol 0 1.00 0.02 FAL: 3980, TRU: 74
mentions_surveillance 0 1.00 0.11 FAL: 3608, TRU: 446
mentions_task_force 0 1.00 0.01 FAL: 4004, TRU: 50
mentions_traffic 0 1.00 0.05 FAL: 3868, TRU: 186

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
year 34 0.99 2019.05 0.92 2008.0 2019.00 2019.00 2019.00 2020.00 ▁▁▁▁▇
month 35 0.99 5.49 3.54 1.0 2.00 5.00 9.00 12.00 ▇▂▃▂▃
day 35 0.99 15.92 8.71 1.0 8.00 16.00 24.00 31.00 ▇▇▇▇▆
hour 84 0.98 11.60 5.43 0.0 9.00 11.00 15.00 23.00 ▂▇▇▅▃
minute 84 0.98 27.00 17.87 0.0 12.00 30.00 43.00 59.00 ▇▆▇▆▅
age 290 0.93 34.73 10.75 1.0 27.00 34.00 41.00 97.00 ▁▇▃▁▁
us_address_zip 627 0.85 95949.26 12037.51 0.0 98125.00 98421.00 98421.00 99999.00 ▁▁▁▁▇
accompanied_juvenile_flag 2 1.00 0.00 0.03 0.0 0.00 0.00 0.00 1.00 ▇▁▁▁▁
unaccompanied_juvenile_flag 2 1.00 0.00 0.03 0.0 0.00 0.00 0.00 1.00 ▇▁▁▁▁
custody_redetermination_flag 2 1.00 0.01 0.09 0.0 0.00 0.00 0.00 1.00 ▇▁▁▁▁
quarter 39 0.99 2019.48 0.92 2008.4 2019.20 2019.40 2020.10 2020.30 ▁▁▁▁▇
fy 39 0.99 2019.24 0.96 2008.0 2019.00 2019.00 2020.00 2020.00 ▁▁▁▁▇
lat 238 0.94 46.94 2.10 26.1 45.63 47.32 47.82 61.22 ▁▁▆▇▁
lon 238 0.94 -121.62 3.19 -149.9 -122.74 -122.31 -120.51 -75.93 ▁▇▂▁▁

Record source

UWCHR’s I-213 collection consists of records produced by both CBP and ICE via FOIA litigation, as described above. CBP records are a sample (every 20th record) of all forms produced by staff in the agency’s Blaine and Spokane sectors from January 1, 2012 to September 30, 2017; ICE records include all documents produced from January 1, 2019 to March 31, 2020 by staff from the agency’s Seattle field office.

As seen below, some ICE records fall out of the window of time covered by the FOIA settlement; closer scrutiny of these records often shows that the I-213 “Date/Time” field (scraped as year, month, day, hour, minute fields; also converted into fiscal year fy) sometimes relates to a prior apprehension, with the date_of_action field more accurately reflecting the date of the apprehension described by the I-213 narrative. Missing “Date/Time” values have also been filled in with date_of_action values where appropriate. This cleaning is ongoing in a separate private repository; cleaning methods and data replacements available for review upon request. For most analysis below we will restrict the dataset to records dated January 1, 2019 and after.

A small number of records with missing source data represent fully-redacted forms produced by ICE; these are excluded in the following analysis. As CBP records are not intended to be comprehensive, and because CBP apprehensions which result in individuals being transferred to ICE custody are represented in the ICE I-213 collection, CBP records are also excluded from the following analysis.

i213 <- i213 %>%
  mutate(across(where(is.character), ~ na_if(.,"")))


ann <- i213 %>%
    filter(!is.na(source)) %>% 
    count(source, year) %>%
    pivot_wider(names_from = source, values_from = n,
                values_fill = list(n = 0)) %>%
    arrange(year)

ann
## # A tibble: 13 × 3
##     year   cbp   ice
##    <dbl> <int> <int>
##  1  2008     0     1
##  2  2010     0     5
##  3  2011     0     1
##  4  2012     1     2
##  5  2013    20     1
##  6  2014    20     3
##  7  2015     9     0
##  8  2016    25     2
##  9  2017    24     5
## 10  2018    41    34
## 11  2019    25  3027
## 12  2020     0   774
## 13    NA     0    32
cbp <- i213 %>%
    filter(source == "cbp")

i213 <- i213 %>%
    filter(source != "cbp")

ICE record release installments

Records were released by ICE to UWCHR in ten installments. Nine initial monthly installments were released from May 2020 to January 2021. Subsequent UWCHR research revealed additional I-213 forms which should have been disclosed per the terms of the FOIA lawsuit settlement; in response, ICE released a final installment in June 2021. Each ICE installment was released as a continuous PDF file. During the file scraping process, each installment was assigned a unique six-digit hash value which we mutate below into its corresponding release date for ease of interpretation.

Comparison of the installments follows. We note that date distribution of the installments is erratic but that the center of the date distributions of the first nine installments roughly advances chronologically through the timeline; while the final installment “back-fills” prior months. Note that the overall monthly distribution of records is relatively consistent. Comparison of the installments along other lines, such as geographic distribution, is possible but not particularly instructive; see “compare-installments.Rmd”.

i213$date <- as.Date(i213$date)

data <- i213 %>%
  select(date, source, installment) %>%
  filter(date >= '2019-01-01',
         date <= '2020-03-31',
         source == 'ice')

data$month <- as.Date(cut(data$date, breaks='month'))

p1 <- data %>% ggplot(aes(x = month)) +
  geom_bar() +
  scale_x_date(labels = scales::date_format("%b"), 
               breaks = "3 month") +
  facet_wrap(~installment) +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p1

p2 <- data %>% ggplot(aes(x = month, fill=installment)) +
  geom_bar(width=10) +
  scale_x_date(labels = scales::date_format("%b"), 
               breaks = "3 month") +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p2

Demographics: Overview

Here we display tables for basic demographic characteristics for subjects of the ICE I-213 forms: ten most common “Country of Citizenship” values; “Age” and “Sex” breakdowns (excluding missing values); and “Complexion”.

“Complexion” values are decoded following “The Border’s Long Shadow” (ACLU of Michigan, 2021).

i213 %>% count(country_of_citizenship, sort=TRUE) %>% 
  mutate(pct = n/sum(n) * 100) %>% 
  head(10)
## # A tibble: 10 × 3
##    country_of_citizenship     n    pct
##    <fct>                  <int>  <dbl>
##  1 MEXICO                  2445 62.9  
##  2 GUATEMALA                260  6.69 
##  3 INDIA                    245  6.30 
##  4 HONDURAS                 129  3.32 
##  5 EL SALVADOR               86  2.21 
##  6 CHINA                     86  2.21 
##  7 CANADA                    41  1.05 
##  8 SOUTH KOREA               41  1.05 
##  9 UKRAINE                   34  0.875
## 10 ROMANIA                   26  0.669
i213 %>%
    filter(!is.na(age), !is.na(sex)) %>%
    mutate(age_group = cut(age,
                           breaks = c(0, 18, 25, 40, 60, Inf),
                           right = FALSE)) %>%
    count(sex, age_group) %>%
    pivot_wider(names_from = sex, values_from = n)
## # A tibble: 5 × 4
##   age_group     F     M     U
##   <fct>     <int> <int> <int>
## 1 [0,18)       21    24    NA
## 2 [18,25)      75   374     1
## 3 [25,40)     234  1337     3
## 4 [40,60)     112   703     1
## 5 [60,Inf)     14    43    NA
good_values <- c("ALBINO",
                 "BLACK",
                 "DARK BROWN",
                 "DARK",
                 "FAIR",
                 "LIGHT BROWN",
                 "LIGHT",
                 "MEDIUM BROWN",
                 "MEDIUM",
                 "OLIVE",
                 "RUDDY",
                 "SALLOW",
                 "YELLOW")

i213 <- i213 %>% mutate(cmplxn = case_when(
         cmplxn %in% good_values ~ cmplxn,
         is.na(cmplxn) ~ NA_character_,
         TRUE ~ "OTHER"))

i213 %>% 
  count(cmplxn, sort = TRUE)
## # A tibble: 13 × 2
##    cmplxn           n
##    <chr>        <int>
##  1 MEDIUM        1680
##  2 <NA>          1038
##  3 MEDIUM BROWN   430
##  4 LIGHT BROWN    290
##  5 LIGHT          160
##  6 FAIR           158
##  7 DARK BROWN      37
##  8 DARK            35
##  9 BLACK           31
## 10 OLIVE           15
## 11 OTHER            9
## 12 RUDDY            2
## 13 SALLOW           2

Apprehension location: “At/Near”

Most forms include an “At/Near” value indicating the location of the apprehension; in some cases the “At/Near” field includes a reference to “See Narrative”, in which case we extract this value from the narrative field or complete by hand. In most cases these values follow a “City, State” format; sometimes codes for specific points of entry (e.g. “PHY” for the Pacific Highway POE in Blaine, WA; or “SEA” for Seatle-Tacoma International Airport in Sea-Tac, WA) or names of specific institutions such as jails or correctional facilities. We have standardized values to “City, State” format and include latitude and longitude values via Google Maps API.

“At/Near” values outside the Pacific Northwest may include subjects apprehended elsewhere and processed by the Seattle ICE Field Office, but I-213 narratives confirm that most apprehensions represented in the dataset occurred in the Pacific Northwest.

i213 %>% count(at_near, sort = TRUE) %>% 
   filter(n >= 20)
## # A tibble: 41 × 2
##    at_near          n
##    <chr>        <int>
##  1 SEATAC, WA     418
##  2 PORTLAND, OR   359
##  3 BLAINE, WA     209
##  4 SUMAS, WA      169
##  5 SALEM, OR      134
##  6 EPHRATA, WA    110
##  7 EUGENE, OR      98
##  8 TACOMA, WA      98
##  9 LYNDEN, WA      96
## 10 SPOKANE, WA     95
## # … with 31 more rows
i213 %>%
   count(state, sort = TRUE)
## # A tibble: 11 × 2
##    state     n
##    <chr> <int>
##  1 WA     2594
##  2 OR      966
##  3 <NA>    235
##  4 ID       46
##  5 AK       14
##  6 CA       12
##  7 TX        7
##  8 PA        6
##  9 AZ        4
## 10 MT        2
## 11 NM        1

Mapping apprehension location

We can map records by their “At/Near” values in a variety of ways. For more examples, see: https://uwchr.github.io/i-213-analysis/map-wa.html

world <- ne_countries(scale = "medium", returnclass = "sf")
states <- st_as_sf(map("state", crs = 4326, plot = FALSE, fill = TRUE))
states <- cbind(states, st_coordinates(st_point_on_surface(states)))
states$ID <- toTitleCase(as.character(states$ID))

pnw_coords <- coord_sf(xlim = c(-130, -112.5), ylim = c(41, 50), expand = FALSE, crs = 4326)

data_at_near <- i213 %>% 
  group_by(at_near, lon, lat) %>% 
  summarise(n = n()) %>% 
  na.omit()

data_at_near <- st_as_sf(data_at_near, coords = c("lon", "lat"),
                  crs = 4326, agr = "constant")

ggplot(data = world) +
    geom_sf() +
    geom_sf(data = states, fill = NA) +
    geom_sf(data = data_at_near, aes(size=n), shape = 21, fill = "red", alpha=.5) +
    pnw_coords +
    ggtitle('I-213 forms by "at_near" latitude/longitude')

Apprehension location by DHS office

I-213 forms include “Location Code” field designating the DHS office and sub-office responsible for the apprehension. Values consist of two three-letter codes separated by a forward-slash (“/”). No public data dictionary translating these codes is known, but they appear to be geographically consistent, as seen below. This analysis suggests that the first part of the code designates the local ICE sub-field office or Border Patrol station, while the second part of the code designates the regional ICE field office or Border Patrol sector, e.g. “RIC/SEA” for Seattle ICE Field Office, Richland, WA Sub-Field Office (largely corresponding a published map of ICE Enforcement and Removal Operations field offices); “EUG/POO” for Portland ICE Office, Eugene, OR Sub-Field Office; “SMS/BLW” for Blaine Sector Border Patrol, Sumas Station; “SPK/SPW” for Spokane Sector Border Patrol, Spokane Station (roughly corresponding to a published table of Border Patrol sectors and stations).

Plotting location codes (with added jitter for visual effect) and simplified codes for DHS offices (with lines encircling points to suggest jurisdictional demarcations) demonstrates the geographic consistency of these values. Some ICE field offices and Border Patrol sectors span multiple counties and states, while others are more localized. Note that the Seattle ICE field office covers the majority of the Seattle “Area of Responsibility” (WA, OR, AK; Alaska records fall under the Anchorage Sub-Field Office, “ANC/ANC”); note also the difference in coverage between the relatively localized Blaine Sector Border Patrol (“BLW”) versus the wide geographic range of the Spokane Sector (“SPW”). Note also the overlap of the Seattle, Portland, and Spokane jurisdictions in Central Washington.

i213 %>% count(location_code, sort = TRUE) %>% 
   filter(n >= 20)
## # A tibble: 16 × 2
##    location_code     n
##    <chr>         <int>
##  1 SEA/SEA         907
##  2 <NA>            601
##  3 POO/POO         584
##  4 EUG/POO         271
##  5 SPK/SPW         263
##  6 SMS/BLW         204
##  7 BLS/BLW         174
##  8 RIC/SEA         167
##  9 YAK/SEA         122
## 10 WNT/SEA          94
## 11 ORV/SPW          91
## 12 BLH/BLW          66
## 13 MED/SEA          56
## 14 PHY/SEA          51
## 15 SPO/SEA          50
## 16 BLA/SEA          30
i213$office <- str_match(i213$location_code, "SEA|POO|BLW|SPW")

loc_codes <- i213 %>% 
  group_by(location_code) %>% 
  summarize(n = n()) %>% 
  arrange(desc(n)) %>% 
  na.omit()

loc_codes_county <- i213 %>% 
  group_by(location_code) %>% 
  summarize(n_counties = n_distinct(county),
            n_state = n_distinct(state)) %>% 
  arrange(desc(n_counties)) %>% 
  na.omit()

top <- unlist(loc_codes[1:20, 'location_code'])

sites <- i213 %>% select(lon, lat, location_code, office) %>% 
  filter(location_code %in% top) %>%
  na.omit()

sites <- st_as_sf(sites, coords = c("lon", "lat"),
                   crs = 4326, agr = "constant")

sites <- cbind(sites, st_coordinates(sites))

# Exclude small number of records outside geographic bounds of Pacific Northwest
sites <- sites[sites$X < -110,]
sites <- sites[sites$X > -130,]
sites <- sites[sites$Y < 50,]
sites <- sites[sites$Y > 42,]

sites <- sites %>% 
  na.omit()

m1 <- ggplot() +
    geom_sf(data = world) +
    geom_sf(data = states, fill = NA) + 
    geom_jitter(data = sites, size = 2, shape = 23, width=.25, height=.25, aes(x = X, y = Y, fill = location_code)) +
    pnw_coords +
    ggtitle('I-213 forms by approx. "at_near" latitude/longitude')

m2 <- ggplot() +
    geom_sf(data = world) +
    geom_sf(data = states, fill = NA) + 
    geom_point(data = sites, size = 2, shape = 23, aes(x = X, y = Y, fill = office)) +
    geom_encircle(data = sites, aes(x = X, y = Y, group=office, col=office)) +
    pnw_coords +
    ggtitle('I-213 forms by "at_near" latitude/longitude')

m1

m2

“Method of Location/Apprehension”

The method_location_apprehension field consists of a set of regular alphanumeric codes that apparently describe how the subject of the form was identified or apprehended. No single source defining these codes is known to be publicly available but several are mentioned in various contexts and others can be tentatively decoded using context clues in I-213 narratives. The ten most common codes follow:

i213 %>%
  filter(!is.na(method_location_apprehension)) %>% 
  count(method_location_apprehension, sort = TRUE) %>% 
   head(10)
## # A tibble: 10 × 2
##    method_location_apprehension     n
##    <chr>                        <int>
##  1 CLC NA                         523
##  2 PB UNKNOWN                     477
##  3 L NA                           460
##  4 NCA NA                         408
##  5 ISP NA                         351
##  6 CFD NA                         255
##  7 CST NA                         174
##  8 OA UNKNOWN                     140
##  9 CLC UNKNOWN                    105
## 10 I                               63

As seen above, most of the codes consist of a primary alphabetic code followed by “NA” or “UNKNOWN”, but others include secodary alpha-numeric codes. In some cases, the significance of these is unknown (e.g. “CLC 511.2.2”, “L 13D”). However, the primary codes can be standardized into a dozen main categories, most of which can be tentatively decoded.

Several of the codes are described in an ICE document obtained via FOIA by the American Immigration Council (see page 44). Three of the codes refer to various jurisdictions of ICE’s Criminal Alien Program (CAP): “CLC”, “CST” and “CFD”, referring to CAP Local (city and county jails and correctional facilities), CAP State (state prisons and corrections), and CAP Federal (federal prisons) respectively. The same document defines “NCA” as “Non-Custodial Arrest” for street arrests; and “L” as “Located” for arrests of fugitives. Review of I-213 narratives is largely consistent with these interpretations. (Other codes mentioned in this document are not represented in the UWCHR I-213 collection.)

The code “OA” is defined as “Other Agency” in reports by the ACLU of Michigan and the American Immigration Council, and reportedly involves apprehensions carried out in support of or with the support of other law enforcement agencies. Review of I-213 narratives is largely consistent with this interpretation; for example, some arrests at courthouses in Central Washington involving collaboration with local law enforcement are coded “OA”.

Other codes are tentatively deciphered as follows based on context clues in I-213 narratives and other information:

  • “PB”: Likely “Patrolling Border”; appears to involve apprehensions of suspected unauthorized border-crossers at or near the physical border.
  • “ISP”: Likely “Inspection”; appears to involve apprehensions during inspection at authorized points of entry (mostly Sea-Tac Int’l Airport and border crossings in Blaine, WA).
  • “TCB”: Likely “Transportation/Bus Check”; almost all records involve immigration checks on board buses in the Spokane, WA area.
  • “OTF”: Likely “Task Force”; appears to involve apprehensions during joint task force operations with local law enforcement.
  • “LEA”: Likely “Law Enforcement Assistance”; appears to involve direct requests for assistance by local law enforcement.
  • “O”: Unknown, possibly “Other”.

For a comparison of these codes with keywords mentioned in I-213 narratives, see: https://uwchr.github.io/i-213-analysis/heatmaps.html

We can examine these “Method of Location/Apprehension” categories along various axes including over time, by “Location Code” as discussed above. We note for example that the Seattle and Portland ICE offices have a very different distribution of categories compared to the Blaine or Spokane Border Patrol Sectors; and that the Blaine Border Patrol Sector and Spokane Border Patrol Sector also differ, with Spokane’s distribution more suggestive of reliance on collaboration with local law enforcement.

Comparison of proportion of these categories by county is also interesting: note high proportion of “PB” (“Patrolling Border”) apprehensions in Northern border counties. Note also the correspondence of the categories “CFD” (“CAP Federal”) and “CST” (“CAP State”) with locations of major prisons: e.g. the Federal Detention Center, SeaTac in King County; or Coyote Ridge Corrections Center in Connel, WA in Franklin County.

i213 <- i213 %>% 
  mutate(method_short = case_when(
           str_detect(method_location_apprehension, "PB") ~ "PB",
           str_detect(method_location_apprehension, "CFD") ~ "CFD",
           str_detect(method_location_apprehension, "CST") ~ "CST",
           str_detect(method_location_apprehension, "CLC") ~ "CLC",
           str_detect(method_location_apprehension, "NCA") ~ "NCA",
           str_detect(method_location_apprehension, "LEA") ~ "LEA",
           str_detect(method_location_apprehension, "OA") ~ "OA",
           str_detect(method_location_apprehension, "OTF") ~ "OTF",
           str_detect(method_location_apprehension, "TCB") ~ "TCB",
           str_detect(method_location_apprehension, "ISP") ~ "ISP",
           str_detect(method_location_apprehension, "L") ~ "L",
           str_detect(method_location_apprehension, "O|0") ~ "O",
           is.na(method_location_apprehension) ~ NA_character_,
            TRUE ~ NA_character_),
         method_verbose = case_when(
           str_detect(method_location_apprehension, "PB") ~ "Patrol Border",
           str_detect(method_location_apprehension, "CFD") ~ "CAP Federal",
           str_detect(method_location_apprehension, "CST") ~ "CAP State",
           str_detect(method_location_apprehension, "CLC") ~ "CAP Local",
           str_detect(method_location_apprehension, "NCA") ~ "Non-Custodial Arrest",
           str_detect(method_location_apprehension, "LEA") ~ "Law Enforcement Assist",
           str_detect(method_location_apprehension, "OA") ~ "Other Agency",
           str_detect(method_location_apprehension, "OTF") ~ "Task Force",
           str_detect(method_location_apprehension, "TCB") ~ "Transportation Check",
           str_detect(method_location_apprehension, "ISP") ~ "Inspection",
           str_detect(method_location_apprehension, "L") ~ "Located",
           str_detect(method_location_apprehension, "O|0") ~ "Other",
           is.na(method_location_apprehension) ~ NA_character_,
            TRUE ~ NA_character_),
         method_int_border = case_when(
           str_detect(method_location_apprehension, "PB") ~ "Border",
           str_detect(method_location_apprehension, "CFD") ~ "Interior",
           str_detect(method_location_apprehension, "CST") ~ "Interior",
           str_detect(method_location_apprehension, "CLC") ~ "Interior",
           str_detect(method_location_apprehension, "NCA") ~ "Interior",
           str_detect(method_location_apprehension, "LEA") ~ "Interior",
           str_detect(method_location_apprehension, "OA") ~ "Interior",
           str_detect(method_location_apprehension, "OTF") ~ "Interior",
           str_detect(method_location_apprehension, "TCB") ~ "Interior",
           str_detect(method_location_apprehension, "ISP") ~ "Border",
           str_detect(method_location_apprehension, "L") ~ "Interior",
           str_detect(method_location_apprehension, "O|0") ~ "Other",
           is.na(method_location_apprehension) ~ NA_character_,
            TRUE ~ NA_character_))

i213$date <- as.Date(i213$date)

data <- i213 %>%
  select(date, state, source, method_location_apprehension, method_short, method_verbose, method_int_border, kww_in_force, office, county) %>%
  filter(!is.na(state),
         date >= '2019-01-01',
         source == 'ice')

data$month <- as.Date(cut(data$date, breaks='month'))

p1 <- data %>% 
  ggplot(aes(x = month)) +
  geom_bar() +
  scale_x_date(labels = scales::date_format("%b"),
               breaks = "1 month") +
  facet_wrap(~method_short) +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p1

p2 <- data %>%
  ggplot(aes(x = method_short)) +
  geom_bar(aes(fill= method_short)) +
  facet_wrap(~office) +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p2

priority_counties <- read_yaml('../../shared/hand/priority_counties.yaml')
priority_counties <- append(priority_counties, 'Whatcom County')

p3 <- data %>% 
  filter(county %in% priority_counties) %>% 
  ggplot(aes(x = county, fill= method_verbose)) +
  geom_bar(position = "fill") +
  scale_y_continuous() +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p3

p4 <- data %>% 
  filter(!county %in% priority_counties) %>%
  ggplot(aes(x = county, fill= method_verbose)) +
  geom_bar(position = "fill") +
  scale_y_continuous() +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p4

p5 <- data %>% 
  filter(county %in% priority_counties) %>%
  ggplot(aes(x = county, fill= method_int_border)) +
  geom_bar() +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p5

data <- i213 %>%
  select(date, state, source, method_location_apprehension, method_short, method_verbose, kww_in_force, office, county, at_near) %>%
  filter(!is.na(state),
         state == 'WA',
         date >= '2019-01-01',
         source == 'ice')

at_near_clc <- data %>% group_by(at_near, method_short) %>% 
  summarise(n = n()) %>% 
  mutate(percent = n/sum(n)) %>% 
  filter(method_short == 'CLC')

at_near_clc <- at_near_clc[order(-at_near_clc$n),]

head(at_near_clc, 10)
## # A tibble: 10 × 4
## # Groups:   at_near [10]
##    at_near          method_short     n percent
##    <chr>            <chr>        <int>   <dbl>
##  1 TACOMA, WA       CLC             51   0.520
##  2 KENNEWICK, WA    CLC             49   0.831
##  3 PASCO, WA        CLC             46   0.719
##  4 RITZVILLE, WA    CLC             46   0.958
##  5 EPHRATA, WA      CLC             39   0.386
##  6 OKANOGAN, WA     CLC             36   0.857
##  7 KENT, WA         CLC             27   0.409
##  8 MOUNT VERNON, WA CLC             24   0.533
##  9 VANCOUVER, WA    CLC             24   0.289
## 10 YAKIMA, WA       CLC             15   0.319
at_near_assist <- data %>% group_by(at_near, method_short) %>% 
  summarise(n = n()) %>% 
  mutate(percent = n/sum(n)) %>% 
  filter(method_short %in% c('LEA', 'OA', 'OTF'))

at_near_assist <- at_near_assist[order(-at_near_assist$n),]
data <- i213 %>%
  select(date, state, source, method_location_apprehension, method_short, method_verbose, kww_in_force, office, county, at_near) %>%
  filter(!is.na(state),
         state == 'OR',
         date >= '2019-01-01',
         source == 'ice')

at_near_clc <- data %>% group_by(at_near, method_short) %>% 
  summarise(n = n()) %>% 
  mutate(percent = n/sum(n)) %>% 
  filter(method_short == 'CLC')

at_near_clc <- at_near_clc[order(-at_near_clc$n),]

head(at_near_clc, 10)
## # A tibble: 10 × 4
## # Groups:   at_near [10]
##    at_near           method_short     n percent
##    <chr>             <chr>        <int>   <dbl>
##  1 PENDLETON, OR     CLC             24  0.889 
##  2 HILLSBORO, OR     CLC             20  0.4   
##  3 PORTLAND, OR      CLC             14  0.0393
##  4 EUGENE, OR        CLC             12  0.124 
##  5 BEND, OR          CLC              5  0.556 
##  6 ALBANY, OR        CLC              4  0.333 
##  7 MCMINNVILLE, OR   CLC              3  0.176 
##  8 OREGON CITY, OR   CLC              3  0.0811
##  9 SPRINGFIELD, OR   CLC              3  0.136 
## 10 COTTAGE GROVE, OR CLC              2  0.182
at_near_assist <- data %>% group_by(at_near, method_short) %>% 
  summarise(n = n()) %>% 
  mutate(percent = n/sum(n)) %>% 
  filter(method_short %in% c('LEA', 'OA', 'OTF'))

at_near_assist <- at_near_assist[order(-at_near_assist$n),]
data <- i213 %>%
  select(date, state, source, method_location_apprehension, method_short, method_verbose, kww_in_force, office, county) %>%
  filter(!is.na(state),
         date >= '2019-01-01',
         source == 'ice')

data$month <- as.Date(cut(data$date, breaks='month'))

p1 <- data %>% 
  filter(!is.na(state),
         state == 'OR') %>% 
  ggplot(aes(x = month)) +
  geom_bar() +
  scale_x_date(labels = scales::date_format("%b"),
               breaks = "1 month") +
  facet_wrap(~method_short) +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p1

p2 <- data %>%
    filter(!is.na(state),
         state == 'OR') %>% 
  ggplot(aes(x = method_short)) +
  geom_bar(aes(fill= method_short)) +
  facet_wrap(~office) +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p2

p3 <- data %>%
    filter(!is.na(state),
         state == 'OR') %>% 
  ggplot(aes(x = county, fill= method_verbose)) +
  geom_bar() +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p3

p4 <- data %>%
    filter(!is.na(state),
         state == 'OR') %>% 
  ggplot(aes(x = county, fill= method_verbose)) +
  geom_bar(position = "fill") +
  scale_y_continuous(labels = scales::percent) +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p4

p5 <- data %>%
  filter(!is.na(state),
           state %in% c("OR", "WA")) %>% 
  ggplot(aes(x = state, fill= method_verbose)) +
  geom_bar() +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p5

p6 <- data %>%
    filter(!is.na(state),
           state %in% c("OR", "WA")) %>% 
  ggplot(aes(x = state, fill= method_verbose)) +
  geom_bar(position = "fill") +
  scale_y_continuous(labels = scales::percent) +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p6

Mapping “Method of Location/Apprehension”

sites <- i213 %>% select(lon, lat, office, method_short) %>% 
  na.omit()

sites <- st_as_sf(sites, coords = c("lon", "lat"),
                   crs = 4326, agr = "constant")

sites <- cbind(sites, st_coordinates(sites))

st_crs(states) <- 4326
st_crs(states$geom) <- 4326
st_crs(sites) <- 4326
st_crs(sites$geometry) <- 4326

m1 <- ggplot() +
    geom_sf(data = world) +
    geom_sf(data = states, fill = NA) + 
    geom_jitter(data = sites, size = 2, shape = 23, width = .1, height = .1, aes(x = X, y = Y, fill = method_short)) +
    pnw_coords +
    facet_wrap(~method_short)
    ggtitle('I-213 forms by approx "at_near" latitude/longitude')
## $title
## [1] "I-213 forms by approx \"at_near\" latitude/longitude"
## 
## attr(,"class")
## [1] "labels"
m1

Demographics: Zooming in

Complexion by location and enforcement type

We can also examine various demographic characteristics in comparison with the above categories; note below the only “Method of Location/Apprehension” category with a majority of apprehensions reported as being of people with “Fair” or “Light” complexion is “ISP” or “Inspection” at authorized points of entry.

p1 <- i213 %>%
  ggplot(aes(x = cmplxn, fill= cmplxn)) +
  geom_bar() +
  facet_wrap(~office) +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p1

p2 <- i213 %>%
  ggplot(aes(x = cmplxn, fill= cmplxn)) +
  geom_bar() +
  facet_wrap(~method_short) +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p2

Country of origin

A few views of most common “Country of Origin” categories. All others have <50 records in collection.

i213 <- i213 %>% 
  mutate(country_short = case_when(
         str_detect(country_of_citizenship, "MEXICO") ~ "MEXICO",
         str_detect(country_of_citizenship, "GUATEMALA") ~ "GUATEMALA",
         str_detect(country_of_citizenship, "INDIA") ~ "INDIA",
         str_detect(country_of_citizenship, "HONDURAS") ~ "HONDURAS",
         str_detect(country_of_citizenship, "EL SALVADOR") ~ "EL SALVADOR",
         str_detect(country_of_citizenship, "CHINA") ~ "CHINA",
         is.na(country_of_citizenship) ~ NA_character_,
         TRUE ~ "ALL OTHERS"))

p1 <- i213 %>%
  ggplot(aes(x = country_short, fill= country_short)) +
  geom_bar() +
  facet_wrap(~office) +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p1

p2 <- i213 %>%
  ggplot(aes(x = country_short, fill= country_short)) +
  geom_bar() +
  facet_wrap(~method_short) +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p2

p3 <- i213 %>% 
  filter(county %in% priority_counties) %>% 
  ggplot(aes(x = county, fill= country_short)) +
  geom_bar(position = "fill") +
  scale_y_continuous(labels = scales::percent) +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p3

i213$month <- as.Date(cut(i213$date, breaks='month'))

p4 <- i213 %>% 
  filter(!is.na(state),
       state == 'WA',
       date >= '2019-01-01',
       source == 'ice') %>% 
  ggplot(aes(x = month)) +
  geom_bar() +
  scale_x_date(labels = scales::date_format("%b"),
               breaks = "1 month") +
  facet_wrap(~country_short) +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p4

A closer look at Whatcom County data:

p5 <- i213 %>% 
  filter(!is.na(state),
       state == 'WA',
       date >= '2019-01-01',
       source == 'ice',
       county == 'Whatcom County') %>% 
  ggplot(aes(x = country_short, fill= country_short)) +
  geom_bar() +
  facet_wrap(~method_int_border) +
  theme(axis.text.x = element_text(angle = 90,
                                   vjust = 0.5,
                                   hjust = 1))

p5

p5_data <- i213 %>% 
  filter(!is.na(state),
       state == 'WA',
       date >= '2019-01-01',
       source == 'ice',
       county == 'Whatcom County') %>% 
  group_by(method_verbose, country_of_citizenship) %>%
  summarise(n = n())

# write_csv(p5_data, '../output/whatcom-method-citizenship.csv')

Mapping “mentions_*”

A few passes at mapping records that mention keywords.

Note I-213s with narratives mentioning keyword “courthouse” include high concentration in central WA; see 2019 UWCHR report on courthouse arrests in WA state.

data <- i213 %>% 
  group_by(mentions_courthouse, lon, lat) %>% 
  summarise(n = n()) %>% 
  filter(mentions_courthouse == TRUE) %>% 
  na.omit()

data <- st_as_sf(data, coords = c("lon", "lat"),
                  crs = 4326, agr = "constant")

ggplot(data = world) +
    geom_sf() +
    geom_sf(data = states, fill = NA) +
    geom_sf(data = data, aes(size=n), shape = 21, fill = "red", alpha=.5) +
    pnw_coords +
    ggtitle('I-213 forms mentioning "courthouse" by "at_near" latitude/longitude')

data <- i213 %>% 
  group_by(mentions_juvenile, lon, lat) %>% 
  summarise(n = n()) %>% 
  filter(mentions_juvenile == TRUE) %>% 
  na.omit()

data <- st_as_sf(data, coords = c("lon", "lat"),
                  crs = 4326, agr = "constant")

ggplot(data = world) +
    geom_sf() +
    geom_sf(data = states, fill = NA) +
    geom_sf(data = data, aes(size=n), shape = 21, fill = "red", alpha=.5) +
    pnw_coords +
    ggtitle('I-213 forms mentioning "juvenile" by "at_near" latitude/longitude')

data <- i213 %>% 
  group_by(mentions_bus, lon, lat) %>% 
  summarise(n = n()) %>% 
  filter(mentions_bus == TRUE) %>% 
  na.omit()

data <- st_as_sf(data, coords = c("lon", "lat"),
                  crs = 4326, agr = "constant")

ggplot(data = world) +
    geom_sf() +
    geom_sf(data = states, fill = NA) +
    geom_sf(data = data, aes(size=n), shape = 21, fill = "red", alpha=.5) +
    pnw_coords +
    ggtitle('I-213 forms mentioning "bus" by "at_near" latitude/longitude')